@kannan_fno | kannansingaravelu.com | github.com/kannansingaravelu
© 2020. All rights reserved.
Quantitative trading consists of trading strategies based on quantitative analysis that rely on mathematics and statistics to identify trading opportunities. Quantitative strategies can be broadly classified into a) Trend Following and b) Mean Reversion.
Almost 85% of CTA returns can be explained by simple trend following. Momentum strategies are by far the most popular systematic strategies used by hedge fund managers. Pair Trading strategies are popular version of statistical arbitrage that employ mean reversion models to capture short term mispricing.
Quantitative strategies involve heavy use of quantitative and computational approaches to trading.
While moving average models are the ‘hello world’ of trend following strategies, in the era of machine learning, I try to approach it purely from a quantitative perspective where all signals were based on raw price data and its statistical properties.
The strategy is to buy and sell the Indian equity benchmark Nifty Index when the trend score is above/below a certain threshold.
Data can be mined from Bloomberg, Broker's API, Data Vendors and Refinitiv's Eikon API. Nifty futures 1-Min data from August 2010 to August 2019 was used for this exercise.
We'll import the required libraries that we'll use.
# Import the pandas library
import pandas as pd
# Import the numpy library
import numpy as np
# Import trend score function from the pyalgo library
from pyalgo.tseries import trend_score
# Import cufflinks library for data visualization
import cufflinks as cf
cf.set_config_file(offline=True)
# Import financial function library for performance metrics
import ffn
# Ignore warnings
import warnings
warnings.filterwarnings("ignore")
We'll read historical intraday data of NIFTY Index.
# Retrieve Data from text file
df = pd.read_csv('data/NIFTY-I-NEW.txt', sep=',', header=None)
df.head()
| date | time | open | high | low | close | volume | datetime | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2010-08-16 | 09:15:59 | 5453.95 | 5453.95 | 5452.5 | 5452.6 | 21200.0 | 2010-08-16 09:15:59 |
| 1 | 2010-08-16 | 09:16:59 | 5453.00 | 5454.00 | 5452.6 | 5454.0 | 21250.0 | 2010-08-16 09:16:59 |
| 2 | 2010-08-16 | 09:17:59 | 5453.90 | 5454.00 | 5451.4 | 5451.4 | 32750.0 | 2010-08-16 09:17:59 |
| 3 | 2010-08-16 | 09:18:59 | 5451.30 | 5453.00 | 5451.0 | 5453.0 | 26850.0 | 2010-08-16 09:18:59 |
| 4 | 2010-08-16 | 09:19:59 | 5452.55 | 5453.95 | 5451.5 | 5452.0 | 22900.0 | 2010-08-16 09:19:59 |
Now, we will resample the data to 5-min interval and manipulate it to generate trade signals.
# Resample to 5-Min
df = (
df.set_index('datetime')
.drop('date',axis=1)
.drop('time',axis=1)
.resample('5min')
.agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'})
.dropna()
)
df.head()
| open | high | volume | low | close | |
|---|---|---|---|---|---|
| datetime | |||||
| 2010-08-16 09:15:00 | 5453.95 | 5454.00 | 124950.0 | 5451.00 | 5452.0 |
| 2010-08-16 09:20:00 | 5452.20 | 5454.60 | 200500.0 | 5445.30 | 5447.1 |
| 2010-08-16 09:25:00 | 5447.30 | 5449.55 | 178450.0 | 5444.25 | 5446.0 |
| 2010-08-16 09:30:00 | 5445.25 | 5451.00 | 154250.0 | 5444.15 | 5449.3 |
| 2010-08-16 09:35:00 | 5449.15 | 5455.60 | 149100.0 | 5449.05 | 5454.9 |
# Plot Index Price
df['close'].iplot(title='Nifty Index from 2010-2019')
We'll define the strategy and rules based on which the trade signals would be generated.
Trend score is a measure of how strongly the time series is trending. Consider a time series of length $n$ with ordered components $\{x_i\}$ for $i=1,...,n$
A score between -1 and +1 can be associated to $\{x_i\}$ based on how strongly the time series is trending. Scores near $\pm$1 will be associated with strongly positively/negatively trending series, and scores near 0 will be assigned to series that do not exhibit trending characteristics. Trend Score determines the trading signal based on the statistical strength of the realized return:
$$TrendScore_i \left\{ \begin{array} {ll} +1 &\mbox{if } score>+1 \\ score & otherwise \\ -1 &\mbox{if }score<-1 \end{array} \right.$$When the absolute value of our trend score is greater than 1, the trend is highly statistically significant, so the strategy puts 100% exposure to the asset.
Enter (Buy) when the trend score is above a specific value. Exit (Sell) the position when the trend score falls below a specific value. The trend duration is specified by another parameter.
# Apply Trend Score
df['tscore'] = df['close'].rolling(60).apply(trend_score, raw=True).fillna(0)
# Check the last five values
df['tscore'].tail()
datetime 2019-08-26 15:05:00 1.0 2019-08-26 15:10:00 1.0 2019-08-26 15:15:00 1.0 2019-08-26 15:20:00 1.0 2019-08-26 15:25:00 1.0 Name: tscore, dtype: float64
Backtesting is a process of applying your trading strategy to historical data and stimulate how they would have performed in the past without actually risking your capital.
# Define signal based on trend score
df['signal'] = np.where(df['tscore']==1,1,0)
df['signal'] = np.where(df['tscore']<0.8,0,df['signal'])
# Calculate strategy return
df['returns'] = (df['signal'].shift(1) * df['close'].pct_change()).fillna(0)
# Construct equity curve
df['eq_curve'] = 1e5 * (1+df['returns']).cumprod()
# Plot Strategy Curve
df[['close', 'eq_curve']].normalize().iplot(title='Strategy Equity Curve')